/*
 * PopupUsers.hpp
 *
 *  Created on: Jul 7, 2012
 *      Author: guillou
 */

#ifndef POPUPUSERS_HPP_
#define POPUPUSERS_HPP_

#include <string>
#include <set>
#include <map>
#include <QMutex>
#include <PopupLibTypes.hpp>
#include <QObject>
#include <QIcon>

struct PopupUsersObserver;

struct PopupUserExt : public Popup::User
{
  PopupUserExt() {}
  PopupUserExt(const Popup::User & p_user) : Popup::User(p_user){}
  virtual ~PopupUserExt() {}
  inline unsigned int statValue(Popup::UserRateItem p_key) const {
    Popup::UserStatistics::const_iterator _it = stats.find(p_key);
    if (_it != stats.end()) {
      return _it->second;
    } else {
      return 0;
    }
  }

  // Multiplicated by 2 => 1 bit shift
  enum { POPUP_USER_FIELD_STATS = Popup::POPUP_USER_FIELD_MAX * 2};

  Popup::UserStatistics stats;
};

class PopupUsersManager : public QObject
{
  Q_OBJECT

private:
  PopupUsersManager() : m_mutex(QMutex::Recursive), m_sessionID(0), m_me(0) {}

public:

  static PopupUsersManager *instance();
  virtual ~PopupUsersManager() {}

  //! Returns user's current session ID
  Popup::SessionID getSessionID() const;

  //! Returns local user ID
  Popup::UserID getMyID() const;

  //! Returns specified user nickname. If p_userID is 0, nickname of the
  //! local user is returned
  std::string getNickname(Popup::UserID p_userID = 0) const;
  //! Returns specified user avatar. If p_userID is 0, avatar of the
  //! local user is returned
  std::string getAvatar(Popup::UserID p_userID = 0) const;
  //! Returns specified user mode. If p_userID is 0, mode of the
  //! local user is returned
  Popup::UserMode getMode(Popup::UserID p_userID = 0) const;
  //! Returns the next mode
  Popup::UserMode getNextMode(Popup::UserMode p_mode) const;
  //! Returns true if the specified user is alive. If p_userID is 0, status of
  //! the local user is returned.
  bool isAlive(Popup::UserID p_userID = 0) const;

  //! Sets local user nickname
  virtual void setNickname(const std::string & p_nickname);
  //! Sets local user avatar
  virtual void setAvatar(const std::string & p_avatar);
  //! Sets local user mode
  virtual void setMode(Popup::UserMode p_mode);

  //! Gets specified user info
  bool getUserInfo(PopupUserExt & p_user,
                   Popup::UserID p_userID = 0,
                   unsigned short p_infomask = Popup::POPUP_USER_FIELD_ALL) const;

  //! Returns the icon corresponding to the mode
  void getIconFromMode(QIcon & p_icon,
                       Popup::UserMode p_mode) const;

  void registerObserver(PopupUsersObserver *p_observer);
  void unregisterObserver(PopupUsersObserver *p_observer);

public slots:
  void slot_onConnectionUpdate(bool p_connected,
                               Popup::User p_myself,
                               Popup::SessionID p_sessionID);
  void slot_updateUser(Popup::User p_user,
                       unsigned short,
                       bool p_isNewUser);
  void slot_updateUserStatistics(Popup::UserID,
                                 Popup::UserStatistics p_stats);

signals:
  void signal_localUserUpdated(const Popup::User*, unsigned short);

private:
  typedef std::map<Popup::UserID, PopupUserExt> PopupUserExtMap;
  //typedef std::map<Popup::UserID, Popup::User> PopupUserExtMap;
  mutable QMutex m_mutex;
  static PopupUsersManager *m_instance;
  Popup::SessionID m_sessionID;
  PopupUserExt   *m_me;
  PopupUserExtMap m_users;
  std::set<PopupUsersObserver*> m_observers;
};

struct PopupUsersObserver
{
  virtual void localUserUpdated(unsigned short /* p_mask */) {};

  virtual void userUpdated(const PopupUserExt* /* p_user */,
                           unsigned short      /* p_updateMask */,
                           bool                /* p_isNewUser */) {};

  virtual void userStatisticsUpdated(const PopupUserExt* /* p_user */,
                                     unsigned short      /* p_updateMask */) {};

  virtual ~PopupUsersObserver() {}
};

namespace Popup
{
  inline Popup::SessionID getSessionID() {
    return PopupUsersManager::instance()->getSessionID();
  }
  inline Popup::UserID getMyID() {
    return PopupUsersManager::instance()->getMyID();
  }
  inline std::string getNickname(Popup::UserID p_userID = 0) {
    return PopupUsersManager::instance()->getNickname(p_userID);
  }
  inline std::string getAvatar(Popup::UserID p_userID = 0) {
    return PopupUsersManager::instance()->getAvatar(p_userID);
  }
  inline UserMode getMode(Popup::UserID p_userID = 0) {
    return PopupUsersManager::instance()->getMode(p_userID);
  }
  inline UserMode getNextMode(Popup::UserMode p_mode) {
    return PopupUsersManager::instance()->getNextMode(p_mode);
  }
  inline bool isAlive(Popup::UserID p_userID = 0) {
    return PopupUsersManager::instance()->isAlive(p_userID);
  }
  inline bool getUserInfo(PopupUserExt & p_user,
                          Popup::UserID p_userID = 0,
                          unsigned short p_infomask = Popup::POPUP_USER_FIELD_ALL) {
    return PopupUsersManager::instance()->getUserInfo(p_user, p_userID,
                                                      p_infomask);
  }
  inline void getIconFromMode(QIcon & p_icon,
                              Popup::UserMode p_mode) {
    return PopupUsersManager::instance()->getIconFromMode(p_icon, p_mode);
  }
  inline void setNickname(const std::string & p_nickname) {
    return PopupUsersManager::instance()->setNickname(p_nickname);
  }
  inline void setAvatar(const std::string & p_avatar) {
    return PopupUsersManager::instance()->setAvatar(p_avatar);
  }
  inline void setMode(Popup::UserMode p_mode) {
    return PopupUsersManager::instance()->setMode(p_mode);
  }
}

#endif /* POPUPUSERS_HPP_ */
